import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patheffects as PathEffects

def plot_3d_parkour(scores):
    """
    绘制跑酷成绩的 3D 柱状图。
    :param scores: 二维数组，四行分别对应难度 ['very_easy', 'easy', 'medium', 'hard']，
                   每行最多 10 个分数，每个分数为字典形式 {玩家名: 数值}
    """
    difficulty_labels = ['very_easy', 'easy', 'medium', 'hard']
    difficulty_indices = np.arange(len(difficulty_labels))
    colors = ['#87CEEB', '#90EE90', 'yellow', 'red']  # 天蓝色和浅绿色

    fig = plt.figure(figsize=(8,6))
    ax = fig.add_subplot(111, projection='3d')
    ax.view_init(elev=25, azim=-77)  # 调整观察角度
    ax.dist = 8  # 原默认值为10
    
    plt.subplots_adjust(left=0, right=1, bottom=0, top=1)  # 去除四周白边

    dx = 0.8  # 难度维度（x轴）的柱子宽度
    dy = 0.7  # 排名维度（y轴）的柱子宽度

    for i, (difficulty, score_dicts) in enumerate(zip(difficulty_indices, scores)):
        # 对字典列表按照数值排序
        sorted_scores = sorted(score_dicts, key=lambda d: list(d.values())[0])
        num_scores = len(sorted_scores)
        x = np.full(num_scores, difficulty*1.5)
        y = np.arange(num_scores)
        z = np.zeros(num_scores)
        dz = np.array([list(d.values())[0] for d in sorted_scores])
        
        # 绘制柱状图，设置边缘颜色和线宽
        ax.bar3d(x, y, z, dx, dy, dz, color=colors[i], shade=True, edgecolor='gray', linewidth=0.5)
        
        for j, d in enumerate(sorted_scores):
            player_name = list(d.keys())[0]
            score_value = list(d.values())[0]
            x_center = difficulty * 1.5 + dx / 2
            y_center = j + dy / 2
            z_top = score_value
            # 在柱子顶部稍微向上偏移1个单位
            txt = ax.text(x_center, y_center, z_top + 1, player_name,
                          ha='center', va='bottom', fontsize=5.5555, color='white')
            txt.set_path_effects([PathEffects.withStroke(linewidth=1, foreground='black')])
            txt.set_zorder(10)  # 确保文本位于前面
    
    ax.set_xlim(-0.5, len(difficulty_labels) * 1.5)
    ax.set_ylim(-0.5, max(len(s) for s in scores) - 0.5)
    ax.set_zlim(0, max(max(list(d.values())[0] for d in s) for s in scores) * 1.1)    
    

    ax.set_xlabel('Difficulty')
    ax.set_ylabel('Rank')
    ax.set_zlabel('Score')
    ax.set_xticks(difficulty_indices * 1.5)
    ax.set_xticklabels(difficulty_labels)
    
    

    plt.title('NewYearParkour Scores', y=1)
    plt.savefig('parkour_scores.png')
    plt.close()

if __name__ == '__main__':
    # 示例数据：每个分数项为字典形式 {玩家名: 数值}
    data = [
        [{"Alice": 90}, {"Bob": 85}, {"Charlie": 88}],  # very_easy
        [{"David": 75}, {"Eva": 80}, {"Frank": 82}, {"Grace": 79}],  # easy
        [{"Helen": 60}, {"Ian": 65}, {"Jack": 70}, {"Kate": 72}, {"Leo": 68}],  # medium
        [{"Mia": 45}, {"Nick": 111}, {"Olivia": 333}]  # hard
    ]

    plot_3d_parkour(data)
